package com.ocean.core.util;

import net.lingala.zip4j.ZipFile;
import net.sf.sevenzipjbinding.ExtractOperationResult;
import net.sf.sevenzipjbinding.IInArchive;
import net.sf.sevenzipjbinding.SevenZip;
import net.sf.sevenzipjbinding.impl.RandomAccessFileInStream;
import net.sf.sevenzipjbinding.simple.ISimpleInArchive;
import net.sf.sevenzipjbinding.simple.ISimpleInArchiveItem;
import org.apache.commons.compress.archivers.sevenz.SevenZArchiveEntry;
import org.apache.commons.compress.archivers.sevenz.SevenZFile;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;

/**
 * Zip操作类
 *
 * @author zhang
 * @since 2023/10/16
 */
public class ZipUtil {

    private static final Logger log = LoggerFactory.getLogger(ZipUtil.class);

    private static final String[] PASSWORD = {null, "123", "1234", "11111", "1314520", "123456", "manyu", "fengying", "657000", "QQ3227872112", "2826018039",
            "xiangchuyuan123", "3569872092", "daomaisima", "PixivXiangchuedge", "kaixin20221117ls", "女王大人", "yezong", "荒漫天堂", "qqqun693923403", "9300"};

    public static void unFile(String sourcePath, String destDirPath, String passWord) {
        String result = "";
        if (sourcePath.toLowerCase().endsWith(".zip")) {
            //Wrong password!
            result = unZip(sourcePath, destDirPath, passWord);
        } else if (sourcePath.toLowerCase().endsWith(".rar")) {
            //java.security.InvalidAlgorithmParameterException: password should be specified
            result = unRar(sourcePath, destDirPath, passWord);
            System.out.println(result);
        } else if (sourcePath.toLowerCase().endsWith(".7z")) {
            //PasswordRequiredException: Cannot read encrypted content from G:\ziptest\11111111.7z without a password
            result = un7z(sourcePath, destDirPath, passWord);
        }
    }


    /**
     * @param source   原始文件路径
     * @param dest     解压路径
     * @param password 解压文件密码(可以为空)
     */
    private static String unZip(String source, String dest, String password) {
        ZipFile zipFile = null;
        String result = "";
        try {
            if (StringUtils.isNotBlank(password)) {
                zipFile = new ZipFile(source, password.toCharArray());
            } else {
                zipFile = new ZipFile(source);
            }
            zipFile.setCharset(StandardCharsets.UTF_8);
            zipFile.extractAll(dest);
        } catch (Exception e) {
            log.error("unZip error", e);
        }
        return result;
    }


    private static String unRar(String source, String dest, String passWord) {
        RandomAccessFile randomAccessFile = null;
        IInArchive inArchive = null;
        try {
            // 第一个参数是需要解压的压缩包路径，第二个参数参考JdkAPI文档的RandomAccessFile
            randomAccessFile = new RandomAccessFile(source, "r");
            if (StringUtils.isNotBlank(passWord))
                inArchive = SevenZip.openInArchive(null, new RandomAccessFileInStream(randomAccessFile), passWord);
            else
                inArchive = SevenZip.openInArchive(null, new RandomAccessFileInStream(randomAccessFile));

            ISimpleInArchive simpleInArchive = inArchive.getSimpleInterface();
            for (final ISimpleInArchiveItem item : simpleInArchive.getArchiveItems()) {
                final int[] hash = new int[]{0};
                if (!item.isFolder()) {
                    ExtractOperationResult result;
                    final long[] sizeArray = new long[1];

                    File outFile = new File(dest + item.getPath());
                    File parent = outFile.getParentFile();
                    if ((!parent.exists()) && (!parent.mkdirs())) {
                        continue;
                    }
                    if (StringUtils.isNotBlank(passWord)) {
                        result = item.extractSlow(data -> {
                            try {
                                IOUtils.write(data, new FileOutputStream(outFile, true));
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                            hash[0] ^= Arrays.hashCode(data); // Consume data
                            sizeArray[0] += data.length;
                            return data.length; // Return amount of consumed
                        }, passWord);
                    } else {
                        result = item.extractSlow(data -> {
                            try {
                                IOUtils.write(data, new FileOutputStream(outFile, true));
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                            hash[0] ^= Arrays.hashCode(data); // Consume data
                            sizeArray[0] += data.length;
                            return data.length; // Return amount of consumed
                        });
                    }

                    if (result == ExtractOperationResult.OK) {
                        log.error("解压rar成功...." + String.format("%9X | %10s | %s", hash[0], sizeArray[0], item.getPath()));
                    } else if (StringUtils.isNotBlank(passWord)) {
                        log.error("解压rar成功：密码错误或者其他错误...." + result);
                        return "password";
                    } else {
                        return "rar error";
                    }
                }
            }

        } catch (Exception e) {
            log.error("unRar error", e);
            return e.getMessage();
        } finally {
            try {
                inArchive.close();
                randomAccessFile.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return "";
    }

    private static String un7z(String sourceRarPath, String dest, String passWord) {
        try {
            File srcFile = new File(sourceRarPath);//获取当前压缩文件
            // 判断源文件是否存在
            if (!srcFile.exists()) {
                throw new Exception(srcFile.getPath() + "所指文件不存在");
            }
            //开始解压
            SevenZFile zIn = null;
            if (StringUtils.isNotBlank(passWord)) {
                zIn = new SevenZFile(srcFile, passWord.toCharArray());
            } else {
                System.out.println("没有密码");
                zIn = new SevenZFile(srcFile);
            }

            SevenZArchiveEntry entry = null;
            File file = null;
            while ((entry = zIn.getNextEntry()) != null) {
                if (!entry.isDirectory()) {
                    file = new File(dest, entry.getName());
                    if (!file.exists()) {
                        new File(file.getParent()).mkdirs();//创建此文件的上级目录
                    }
                    OutputStream out = new FileOutputStream(file);
                    BufferedOutputStream bos = new BufferedOutputStream(out);
                    int len = -1;
                    byte[] buf = new byte[1024];
                    while ((len = zIn.read(buf)) != -1) {
                        bos.write(buf, 0, len);
                    }
                    // 关流顺序，先打开的后关闭
                    bos.close();
                    out.close();
                }
            }
        } catch (Exception e) {
            log.error("password {}", passWord);
            log.error("un7z is error", e);
        }
        return "";
    }


    public static void main(String[] args) {
        for (String s : PASSWORD) {
            unFile("F:\\Tencent Message\\3269199948\\FileRecv\\vam荣荣舞啪.zip",
                    "F:\\Tencent Message\\3269199948\\FileRecv",
                    s);
        }
        System.out.println("DONE!");
    }
}
